package main

import (
	"fmt"
	"math"
	"reflect"
	"runtime"
)

func eval(a, b int, op string) (int, error) {
	var result int
	switch op {
	case "+":
		result = a + b
	case "-":
		result = a - b
	case "*":
		result = a * b
	case "/":
		//result = a / b
		result, _ = div(a, b)
	default:
		return 0, fmt.Errorf("错误的操作符：%s", op)
	}
	// nil表示没错
	return result, nil
}

// 定义方法一
/*func div(a, b int) (int, int) {
	return a / b, a % b
}*/
// 定义方法二（建议）可以给返回值取个名字方便读
func div(a, b int) (q, r int) {
	return a / b, a % b
}

// 定义方法三（不建议）
/*func div(a, b int) (q, r int) {
	q = a / b
	r = a % b
	return
}*/

/**
- 函数可以返回多个值
- 函数返回值多个值的时候可以取名字
- 取了名字，直接给名字赋值之后直接return，仅用于非常简单函数（如上定义div函数的第三种方式）
- 取名字对调用者没区别
- 通常情况下，函数返回值第一个返回正常要返回的，第二个返回一个error，然后让调用者具体来处理这个error
- go语言是一个函数式编程的语言，它的函数是一等公民，函数的参数、函数的返回值都可以是函数，函数体里面也可以有函数
*/

func apply(op func(int, int) int, a, b int) int {
	p := reflect.ValueOf(op).Pointer() // 获取这个函数的真正的一个指针
	opName := runtime.FuncForPC(p).Name()
	fmt.Printf("Calling function %s with args (%d, %d)\n", opName, a, b)
	return op(a, b)
}

func pow(a, b int) int {
	return int(math.Pow(float64(a), float64(b)))
}

/**
go函数有可变参数列表
*/
func sum(numbers ...int) int {
	s := 0
	for i := range numbers {
		s += numbers[i]
	}
	return s
}

func main() {
	//fmt.Println(eval(13, 4, "/"))
	//fmt.Println(div(13, 3))
	// 接收函数的多个返回值
	//q, r := div(13, 3)
	//fmt.Println(q, r)
	// 只取一个,不要的英文下划线舍弃

	// go的代码规范，返回值第二个error
	/*fmt.Println(eval(13, 4, "x"))
	if result, err := eval(13, 4, "x"); err != nil {
		fmt.Println("Error:", err)
	} else {
		fmt.Println(result)
	}*/

	// 直接传入一个函数的名字
	fmt.Println(apply(pow, 3, 4))
	// 直接可以使用匿名函数
	fmt.Println(apply(func(a, b int) int {
		return int(math.Pow(float64(a), float64(b)))
	}, 3, 4))

	fmt.Println(sum(1, 2, 3, 4, 5))

}

/**
要点回顾：
- 返回值类型写在最后面
- 可返回多个值
- 函数可以作为参数
- 没有默认参数，可选参数，没有函数重载
- go函数只有一个”可变参数列表“一个高级常用的特征，没有其他花哨的东西
*/
